﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PubSubLite.Utils;
using PubSubLite.Messaging.MessageChannels;
using PubSubLite.Configuration;

namespace PubSubLite.Messaging
{
    /// <summary>
    /// This class is responsible for routing messages and commands from the bus to the message channel.
    /// </summary>
    public class MessageRouter : IMessageRouter
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="configuration"></param>
        public MessageRouter(IMessageChannelProvider channelProvider, IBusConfiguration configuration)
            : this(channelProvider, configuration, SubscribersStorage.Default)
        {

        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="configuration"></param>
        /// <param name="storage"></param>
        public MessageRouter(IMessageChannelProvider channelProvider, IBusConfiguration configuration, ISubscribersStorage storage)
        {
            Require.Argument.IsNotNull(channelProvider, "channelProvider");
            Require.Argument.IsNotNull(configuration, "configuration");
            Require.Argument.IsNotNull(storage, "storage");
            _channelProvider = channelProvider;
            _configuration = configuration;
            _subscribersStorage = storage;
        }

        private IMessageChannelProvider _channelProvider;
        private IBusConfiguration _configuration;
        private ISubscribersStorage _subscribersStorage;

        private void Send(IMessageBase message)
        {
            foreach (var channel in _channelProvider.Channels)
            {
                _configuration.Debug("Sending on channel of type " + channel.GetType().Name);
                channel.Send(message);
            }
        }

        /// <summary>
        /// Routes a command
        /// </summary>
        /// <typeparam name="T">Command type</typeparam>
        /// <typeparam name="T2">Command reply type</typeparam>
        /// <param name="message"></param>
        public void RouteCommand<T, T2>(ICommandMessage<T, T2> message)
        {
            if (message.IsCommand)
            {
                _configuration.Debug("dispatching command from router, messageID: " + message.MessageID.ToString());
                var subscription = _subscribersStorage.GetSubscribers<T>().FirstOrDefault();
                if (subscription != null)
                {
                    _subscribersStorage.AddCommandCallback<T2>(message.MessageID, message.Callback);
                    Send(message);
                }
            }
        }

        /// <summary>
        /// Routes a message
        /// </summary>
        /// <typeparam name="T">Message type</typeparam>
        /// <param name="message"></param>
        public void RouteMessage<T>(IMessage<T> message)
        {
            _configuration.Debug("routing message to channel");
            Send(message);
        }
    }
}
